package cc.shacocloud.mirage.restful;

import cc.shacocloud.mirage.restful.bind.DefaultWebDataBinderFactory;
import cc.shacocloud.mirage.restful.bind.WebBindingInitializer;
import cc.shacocloud.mirage.restful.bind.WebDataBinderFactory;
import cc.shacocloud.mirage.restful.bind.annotation.PathPatterns;
import cc.shacocloud.mirage.utils.MethodParameter;
import cc.shacocloud.mirage.utils.reflection.ParameterNameDiscoverer;
import io.vertx.core.Future;
import io.vertx.core.http.HttpMethod;
import io.vertx.ext.web.Route;
import io.vertx.ext.web.Router;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 基于 vertx {@link Router} 的{@link RequestMappingInfo} 请求映射处理器
 *
 * @author 思追(shaco)
 */
public class VertxRouterRequestMappingMappingRequestHandler extends AbstractRequestMappingHandlerMapping
        implements RouterMappingHandler {
    
    // 参数名称获取器
    @NotNull
    protected final ParameterNameDiscoverer parameterNameDiscoverer;
    // 处理器执行链映射
    private final List<HandlerMethodMapping> handlerMethodMappings = new ArrayList<>(128);
    // web 绑定初始化器
    @Getter
    @Nullable
    protected WebBindingInitializer webBindingInitializer;
    // 处理器方法参数解析器
    @Nullable
    protected HandleMethodArgumentResolverComposite argumentResolverComposite;
    // 处理器方法结果值处理器
    @Nullable
    protected HandleMethodReturnValueHandlerComposite returnValueHandlerComposite;
    // 处理拦截器
    @Nullable
    protected HandlerInterceptorComposite interceptorComposite;
    // 处理异常解析器
    @Nullable
    protected HandlerExceptionResolverComposite exceptionResolverComposite;
    
    public VertxRouterRequestMappingMappingRequestHandler(@NotNull ParameterNameDiscoverer parameterNameDiscoverer) {
        this.parameterNameDiscoverer = parameterNameDiscoverer;
    }
    
    @Override
    public Future<Void> bindRouterMapping(Router router) {
        // 循环所有的路由映射
        for (HandlerMethodMapping handlerMethodMapping : this.handlerMethodMappings) {
            RequestMappingInfo mapping = handlerMethodMapping.getMapping();
            HandlerExecutionChain handler = createHandlerExecutionChain(
                    createInvocableHandlerMethod(handlerMethodMapping.getHandlerMethod()), mapping);
            
            bindRouterHandler(router, mapping, handler);
        }
        
        return Future.succeededFuture();
    }
    
    /**
     * 绑定路由处理器
     *
     * @param router  路由器
     * @param mapping 请求映射
     * @param handler 请求响应处理器
     */
    protected void bindRouterHandler(@NotNull Router router, @NotNull RequestMappingInfo mapping, @NotNull HandlerExecutionChain handler) {
        for (String path : mapping.getPaths()) {
            Route route = router.route().useNormalizedPath(true);
            
            // 获取路由并且注册对应的路径匹配模式
            if (PathPatterns.REGEX.equals(mapping.getPathPatterns())) {
                route.pathRegex(path);
            } else {
                route.path(path);
            }
            
            // 注册请求方式
            Arrays.stream(mapping.getMethods()).forEach(method -> route.method(HttpMethod.valueOf(method.name())));
            // 注册请求类型
            Arrays.stream(mapping.getConsumes()).forEach(route::consumes);
            // 注册响应类型
            Arrays.stream(mapping.getProduces()).forEach(route::produces);
            // 设置处理器方法
            route.failureHandler(handler::processException).handler(handler::handle);
        }
    }
    
    @Override
    protected void registerHandlerMethod(HandlerMethod handlerMethod, RequestMappingInfo mapping) {
        this.handlerMethodMappings.add(new HandlerMethodMapping(handlerMethod, mapping));
    }
    
    /**
     * 根据给定的{@link VertxInvokeHandler} 和 {@link RequestMappingInfo} 创建 {@link HandlerExecutionChain}
     */
    protected HandlerExecutionChain createHandlerExecutionChain(VertxInvokeHandler invokeHandler,
                                                                RequestMappingInfo mapping) {
        HandlerExecutionChain handlerExecutionChain = new HandlerExecutionChain(invokeHandler, mapping, this.interceptorComposite);
        
        if (this.exceptionResolverComposite != null) {
            handlerExecutionChain.setExceptionResolverComposite(exceptionResolverComposite);
        }
        
        return handlerExecutionChain;
    }
    
    /**
     * 根据给定的{@link HandlerMethod}定义创建{@link VertxInvocableHandlerMethod}
     */
    protected VertxInvokeHandler createInvocableHandlerMethod(@NotNull HandlerMethod handlerMethod) {
        VertxInvocableHandlerMethod invocableHandlerMethod = new VertxInvocableHandlerMethod(handlerMethod);
        bindInvocableHandlerMethod(invocableHandlerMethod);
        return invocableHandlerMethod;
    }
    
    /**
     * 绑定 {@link VertxInvocableHandlerMethod}
     */
    protected void bindInvocableHandlerMethod(@NotNull VertxInvocableHandlerMethod invocableHandlerMethod) {
        invocableHandlerMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
        invocableHandlerMethod.setWebDataBinderFactory(createDataBinderFactory());
        
        if (this.argumentResolverComposite != null) {
            invocableHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolverComposite);
        }
        
        if (this.returnValueHandlerComposite != null) {
            invocableHandlerMethod.setReturnValueHandlers(returnValueHandlerComposite);
        }
    }
    
    /**
     * 创建一个新的 {@link WebDataBinderFactory}实例
     *
     * @return 默认的 {@link DefaultWebDataBinderFactory}
     */
    protected WebDataBinderFactory createDataBinderFactory() {
        return new DefaultWebDataBinderFactory(webBindingInitializer);
    }
    
    @Override
    protected void validateMethodMapping(@NotNull HandlerMethod handlerMethod, RequestMappingInfo mapping) {
        MethodParameter returnType = handlerMethod.getReturnType();
        Class<?> resultType = returnType.getParameterType();
        if (!Future.class.isAssignableFrom(resultType)) {
            throw new IllegalArgumentException(handlerMethod.getBean() + "'的方法 \n" + handlerMethod + " 返回值类型：["
                    + resultType + "] 不符合预期，@RequestMapping的返回值类型必须是 'io.vertx.core.Future' 或其子类!");
        }
        
        super.validateMethodMapping(handlerMethod, mapping);
    }
    
    @Setter
    @Getter
    @NoArgsConstructor
    @AllArgsConstructor
    public static class HandlerMethodMapping {
        
        private HandlerMethod handlerMethod;
        
        private RequestMappingInfo mapping;
        
    }
    
}
